import Node from './node';
import { getNodesMinEnclosingRect, getType, log, nodesToMap, nodesTreeToList, transformedCoord } from '@/utils';

/**
 * 编组类
 */
export default class Group extends Node {
  className = 'Group'; // 类名
  nodes = []; // 子节点
  flipX = false; // 编组不可翻转
  flipY = false; // 编组不可翻转
  // 编组没有阴影
  shadowOffsetX = 0;
  shadowOffsetY = 0;
  shadowBlur = 0;
  shadowColor = 'transparent';
  constructor(config = {}) {
    super(config);
  }
  // 获取子节点的映射对象
  get nodesMap() {
    return nodesToMap(this.nodes);
  }
  // 可见子节点
  get visibleNodes() {
    return this.nodes.filter((i) => i.visible);
  }
  // 当前鼠标悬停位置的节点
  get hoverNode() {
    return this.visibleNodes.findLast((node) => node.isPointIn()) || this;
  }
  // 获取所有子节点
  get allNodes() {
    return nodesTreeToList(this.nodes);
  }
  // 获取所有子节点的映射对象
  get allNodesMap() {
    return nodesToMap(this.allNodes);
  }
  // 更新编组
  update() {
    // 没有子节点，移除当前编组
    if (!this.nodes.length) {
      this.remove(true);
      return;
    }
    // 按照深度属性排序子节点
    this.nodes.sort((a, b) => a.zIndex - b.zIndex);
    const { x, y, rotation, scale, nodes, visibleNodes } = this;
    nodes.forEach((node) => ((node.x += x), (node.y += y)));
    // 获取编组子节点在编组父级节点坐标系中的最小外接矩形
    const nodesMinEnclosingRect = getNodesMinEnclosingRect(visibleNodes);
    if (!nodesMinEnclosingRect) {
      nodes.forEach((node) => ((node.x -= x), (node.y -= y)));
      return;
    }
    const { left, top, width, height } = nodesMinEnclosingRect;
    // 校正子节点位置
    nodes.forEach((node) => ((node.x -= left), (node.y -= top)));
    // 计算更新前后位置偏移量
    const offset = transformedCoord({ x: left - x, y: top - y }, { x: 0, y: 0 }, rotation, scale);
    // 计算更新前编组相对父级坐标
    const beforeCenter = { x: x + this.width / 2, y: y + this.height / 2 };
    const before = transformedCoord({ x, y }, beforeCenter, rotation, scale);
    // 计算更新后编组相对父级坐标
    const afterCenter = { x: left + width / 2, y: top + height / 2 };
    const after = transformedCoord({ x: left, y: top }, afterCenter, rotation, scale);
    // 更新编组区域位置、宽度及高度
    this.x = left + offset.x + (before.x - after.x);
    this.y = top + offset.y + (before.y - after.y);
    this.width = width;
    this.height = height;
  }
  // 绘制主要内容
  drawMain(ctx, isExport = false) {
    // 绘制所有子节点
    this.nodes.forEach((node) => node.draw(ctx));
  }
  /**
   * Methods
   */
  /**
   * 添加节点
   * @param  {...*} nodes
   */
  add(...nodes) {
    let onlyAdd = false;
    const last = nodes[nodes.length - 1];
    if (getType(last) === 'Boolean') (onlyAdd = last), nodes.pop();
    let nextZIndex = this.nodes.length ? Math.max(...this.nodes.map((i) => i.zIndex)) + 1 : 0;
    nodes.forEach((node) => {
      if (this.nodes.includes(node)) {
        log.warn(node, '节点重复！已忽略添加此节点。');
        return;
      }
      node.parent = this;
      if (getType(node.zIndex) !== 'Number') node.zIndex = nextZIndex++;
      this.nodes.push(node);
    });
    this.update();
    if (onlyAdd) return;
    // 重绘
    this.stage?.draw();
    this.stage?.handleRecord();
  }
  /**
   * 寻找节点
   * @param {string | function} selector
   */
  find(selector) {
    // @ts-ignore
    if (getType(selector) === 'Function') return this.allNodes.filter(selector);
    if (getType(selector) === 'String') {
      // @ts-ignore
      const firstChar = selector.slice(0, 1);
      // @ts-ignore
      const afterStr = selector.slice(1, selector.length);
      const key = { '#': 'id', '.': 'name' }[firstChar] || 'className';
      const value = { '#': afterStr, '.': afterStr }[firstChar] || selector;
      return this.allNodes.filter((node) => node[key] === value);
    }
  }
  /**
   * 寻找单个节点
   * @param {string | function} selector
   */
  findOne(selector) {
    return this.find(selector)?.[0] || null;
  }
  /**
   * 解编组
   */
  unGroup() {
    const { x, y, width, height, rotation, scale, nodes, parent } = this;
    if (!parent) return;
    nodes.forEach((node) => {
      // 节点中心点坐标
      const center = { x: node.x + node.width / 2, y: node.y + node.height / 2 };
      // 计算节点在编组中旋转缩放后的中心点坐标
      const transformedCenter = transformedCoord(center, { x: width / 2, y: height / 2 }, rotation, scale);
      // 计算节点旋转缩放后位置偏移量
      const offset = { x: transformedCenter.x - center.x, y: transformedCenter.y - center.y };
      parent.add(node, true);
      node.x += x + offset.x;
      node.y += y + offset.y;
      node.rotation += rotation;
      node.scale *= scale;
    });
    this.nodes = [];
    this.remove();
    return nodes;
  }
  /**
   * Static Methods
   */
  /**
   * 编组
   * @param {any[]} nodes
   * @param {*} overrideConfig
   */
  static group([...nodes] = [], overrideConfig) {
    if (!nodes?.length) {
      log.warn(`[Group]\n`, '编组子节点不能为空！');
      return;
    }
    const { parent } = nodes[0];
    if (!nodes.every((node) => node.parent === parent)) {
      log.warn(`[Group]\n`, '编组子节点必须属于同一个父级！');
      return;
    }
    // 将要编组的节点从父级中移除
    nodes.forEach((node) => node.remove(true));
    // 将节点添加到新的编组实例中
    const group = new this({
      // 编组层级取节点中层级最大值
      zIndex: Math.max(...nodes.map((i) => i.zIndex)),
      // 所有节点都可拖动，编组默认可拖动
      draggable: nodes.every((i) => i.draggable),
      ...overrideConfig,
    });
    group.add(...nodes, true);
    parent.add(group, true);
    const stage = parent.className === 'Stage' ? parent : parent.stage;
    if (!stage) return group;
    // 转换器中有编组的节点，设置已选节点为编组节点
    if (stage.transformer.nodes.some((i) => nodes.some((j) => i === j))) stage.setSelectedNodes([group]);
    else {
      // 重绘
      stage.draw();
      stage.handleRecord();
    }
    return group;
  }
}
